#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
/*for getting file size using stat()*/
#include<sys/stat.h>

/*for sendfile()*/
#include<sys/sendfile.h>

/*for O_RDONLY*/
#include<fcntl.h>

// 解析被动模式响应，获取端口号
int parse_pasv_response(char *response) {
    int ip1, ip2, ip3, ip4, port_high, port_low;
    if (sscanf(response, "227 Entering active Mode (%d,%d,%d,%d,%d,%d)", &ip1, &ip2, &ip3, &ip4, &port_high, &port_low) == 6) {
        return port_high * 256 + port_low;
    }
    return -1;
}

// 处理被动模式
int handle_pasv(int sock, const char *ipaddr) {
    char buf[100];
    strcpy(buf, "pasv");
    send(sock, buf, 100, 0);

    recv(sock, buf, 100, 0);
    int port = parse_pasv_response(buf);
    if (port == -1) {
        printf("Failed to parse active mode response\n");
        return -1;
    }

    // 创建新的套接字连接服务器的被动端口
    int data_sock = socket(AF_INET, SOCK_STREAM, 0);
    if (data_sock == -1) {
        perror("Data socket creation failed");
        return -1;
    }

    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(ipaddr);

    if (connect(data_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
        perror("Connect to active port failed");
        close(data_sock);
        return -1;
    }

    return data_sock;
}

// 移除未使用的参数
int main() {
    struct sockaddr_in server;
    struct stat obj;
    int sock;
    int choice;
    char ipaddr[15], buf[100], filename[200], *f;
    int k, size, status, port;
    int filehandle;
    char conn_mode[10];  // 存储连接模式，passive 或 active
    int client_port = 0; // 被动模式下客户端开放的端口

    sock = socket(AF_INET, SOCK_STREAM, 0);

    if(sock == -1)
    {
        printf("socket creation failed");
        exit(1);
    }

    bzero(&server, sizeof(server));
    printf("Enter the port number: ");
    scanf("%d",&port);
    printf("\nEnter the ip address of remote server: ");
    scanf("%s",ipaddr);

    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ipaddr);

    k = connect(sock,(struct sockaddr*)&server, sizeof(server));
    if(k == -1)
    {
        printf("\nConnect Error\n");
        exit(1);
    }

    // 选择连接模式
    printf("Choose connection mode (passive/active): ");
    scanf("%s", conn_mode);

    if (strcmp(conn_mode, "active") == 0) {
        // 被动模式下输入客户端要开放的端口
        printf("Enter the port number the client will open: ");
        scanf("%d", &client_port);
    }

    // 提示用户输入用户名和密码
    char username[20], password[20];
    printf("Enter username: ");
    scanf("%s", username);
    printf("Enter password: ");
    scanf("%s", password);

    // 构造包含用户名和密码的消息，同时添加连接模式信息
    char auth_msg[100];
    if (strcmp(conn_mode, "active") == 0) {
        sprintf(auth_msg, "auth %s %s active %d", username, password, client_port);
    } else {
        sprintf(auth_msg, "auth %s %s passive", username, password);
    }
    // 发送认证消息给服务器
    send(sock, auth_msg, strlen(auth_msg) + 1, 0);

    // 接收服务器的认证结果
    int auth_status;
    recv(sock, &auth_status, sizeof(int), 0);
    if(!auth_status)
    {
        char error_msg[100];
        recv(sock, error_msg, sizeof(error_msg), 0);
        printf("%s\n", error_msg);
        close(sock);
        exit(1);
    }
    printf("Authentication successful.\n");

    int i = 1;
    while(1)
    {
        printf("Enter a choice:\n1- get\n2- put\n3- pwd\n4- dir\n5- cd\n6- mkdir\n7- quit\n8- ?\n");
        scanf("%d", &choice);
        switch(choice)
        {
            //when 'get' command is entered
            case 1:
                if (strcmp(conn_mode, "active") == 0) {
                    // 主动模式下的 get 处理
                    printf("Enter filenames to get (separated by space): ");
                    scanf(" %[^\n]", filename);
                    strcpy(buf, "get ");
                    strcat(buf, filename);
                    send(sock, buf, 100, 0);

                    char *token = strtok(filename, " ");
                    while (token != NULL) {
                        recv(sock, &size, sizeof(int), 0);
                        if(!size)
                        {
                            printf("No such file on the remote directory: %s\n\n", token);
                        } else {
                            f = malloc(size);
                            recv(sock, f, size, 0);
                            while(1)
                            {
                                filehandle = open(token, O_CREAT | O_EXCL | O_WRONLY, 0666);
                                if(filehandle == -1)
                                {
                                    sprintf(token + strlen(token), "%d", i);
                                }
                                else break;
                            }
                            write(filehandle, f, size);
                            close(filehandle);
                            strcpy(buf, "cat ");
                            strcat(buf, token);
                            system(buf);
                        }
                        token = strtok(NULL, " ");
                    }
                } else {
                    // 主动模式下的 get 处理
                    printf("Enter filenames to get (separated by space): ");
                    scanf(" %[^\n]", filename);
                    strcpy(buf, "get ");
                    strcat(buf, filename);
                    send(sock, buf, 100, 0);

                    char *token = strtok(filename, " ");
                    while (token != NULL) {
                        recv(sock, &size, sizeof(int), 0);
                        if(!size)
                        {
                            printf("No such file on the remote directory: %s\n\n", token);
                        } else {
                            f = malloc(size);
                            recv(sock, f, size, 0);
                            while(1)
                            {
                                filehandle = open(token, O_CREAT | O_EXCL | O_WRONLY, 0666);
                                if(filehandle == -1)
                                {
                                    sprintf(token + strlen(token), "%d", i);
                                }
                                else break;
                            }
                            write(filehandle, f, size);
                            close(filehandle);
                            strcpy(buf, "cat ");
                            strcat(buf, token);
                            system(buf);
                        }
                        token = strtok(NULL, " ");
                    }
                }
                break;

            //when 'put' command is entered
            case 2:
                if (strcmp(conn_mode, "active") == 0) {
                    printf("Enter filenames to put to server (separated by space): ");
                    scanf(" %[^\n]", filename);

                    char *token = strtok(filename, " ");
                    while (token != NULL) {
                        filehandle = open(token, O_RDONLY);
                        if(filehandle == -1)
                        {
                            printf("No such file on the local directory: %s\n\n", token);
                        } else {
                            strcpy(buf, "put ");
                            strcat(buf, token);
                            send(sock, buf, 100, 0);
                            stat(token, &obj);
                            size = obj.st_size;
                            send(sock, &size, sizeof(int), 0);
                            sendfile(sock, filehandle, NULL, size);
                            recv(sock, &status, sizeof(int), 0);
                            if(status)
                                printf("File stored successfully: %s\n", token);
                            else
                                printf("File failed to be stored to remote machine: %s\n", token);
                        }
                        close(filehandle);
                        token = strtok(NULL, " ");
                    }
                } else {
                    // 主动模式下的 put 处理
                    printf("Enter filenames to put to server (separated by space): ");
                    scanf(" %[^\n]", filename);

                    char *token = strtok(filename, " ");
                    while (token != NULL) {
                        filehandle = open(token, O_RDONLY);
                        if(filehandle == -1)
                        {
                            printf("No such file on the local directory: %s\n\n", token);
                        } else {
                            strcpy(buf, "put ");
                            strcat(buf, token);
                            send(sock, buf, 100, 0);
                            stat(token, &obj);
                            size = obj.st_size;
                            send(sock, &size, sizeof(int), 0);
                            sendfile(sock, filehandle, NULL, size);
                            recv(sock, &status, sizeof(int), 0);
                            if(status)
                                printf("File stored successfully: %s\n", token);
                            else
                                printf("File failed to be stored to remote machine: %s\n", token);
                        }
                        close(filehandle);
                        token = strtok(NULL, " ");
                    }
                }
                break;

            //when 'pwd' is entered
            case 3:
                strcpy(buf, "pwd");
                send(sock, buf, 100, 0);
                recv(sock, buf, 100, 0);
                printf("The path of the remote directory is: %s\n", buf);
                break;

            //when 'ls' is entered
            case 4:
                strcpy(buf, "dir");
                send(sock, buf, 100, 0);
                recv(sock, &size, sizeof(int), 0);
                f = malloc(size);
                recv(sock, f, size, 0);
                filehandle = creat("temps.txt", S_IWUSR|S_IRUSR);
                write(filehandle, f, size);
                close(filehandle);
                printf("The remote directory listing is as follows:\n");
                system("cat temps.txt");
                printf("\n");
                break;

            //when 'cd' is entered
            case 5:
                strcpy(buf, "cd ");
                printf("Enter the path to change the remote directory: ");
                scanf("%s", buf + 3);
                send(sock, buf, 100, 0);
                recv(sock, &status, sizeof(int), 0);
                if(status)
                    printf("Remote directory successfully changed\n");
                else
                    printf("Remote directory failed to change\n");
                break;

            //when 'mkdir' is entered
            case 6:
                strcpy(buf, "mkdir ");
                printf("Enter the directory name to create: ");
                scanf("%s", buf + strlen(buf));
                send(sock, buf, 100, 0);
                recv(sock, &status, sizeof(int), 0);
                if (status) {
                    printf("Directory created successfully.\n");
                } else {
                    printf("Failed to create directory.\n");
                }
                break;

            //when 'quit' is chosen
            case 7:
                strcpy(buf, "quit");
                send(sock, buf, 100, 0);
                recv(sock, &status, 100, 0);
                if(status)
                {
                    printf("Server closed\nQuitting..\n");
                    exit(0);
                }
                printf("Server failed to close connection\n");
                // 添加 break 语句
                break;
            case 8:
                printf("Commands available are: 1-get <file1> <file2> ...\n 2-put <file1> <file2> ... \n3-pwd \n4-dir \n5-cd <router> \n6-mkdir <dirname> \n7-quit \n8-?\n");
                break;
            //for any wrong value
            default: printf("\nEnter Correct Choice..\n"); break;
        }
    }
}